/**
 * @file JSONDocument.cpp
 */
#include "JSONDocument.h"
#include "JSONNode.h"
#include "JSONHandler.h"
#include "JSONException.h"
#include "JSONQuickParser.h"
#include "../common/CppMemPool.h"
#include "../collections/tree/MultiChildTree.h"

using namespace cppflib::collections::tree;
using namespace cppflib::io;

namespace cppflib
{

namespace json
{

JSONDocument::JSONDocument(void)
{
   pTree = new MultiChildTree(false); // allow duplicated child keys
   cppmempool::Retain(pTree);
}

JSONDocument::~JSONDocument(void)
{
   cppmempool::Release(pTree);
}

template <typename VALTYPE>
PRIVATE void __SetValue(JSONNode * pNode, CFString *pName, VALTYPE v)
{
   if (pNode->GetNodeType() == JSONNode::JN_OBJECT)
      pNode->CastToJSONObject()->Set(pName, v);
   else 
      pNode->CastToJSONArray()->Append(v);
}


/**
 *  JSON handler to parse content for a Document tree
 */
class DocumentJSONHandler : public JSONHandler
{
private:
    MultiChildTree *pTree;
    JSONNode *pCurNode;

private:
    DocumentJSONHandler() { }

    CFString * GetCurNodeName()
    {
       if (pCurNode->GetNodeType() == JSONNode::JN_OBJECT) 
          return new CFString(GetCurNVPairName());
       return NULL;
    }

public:
    explicit DocumentJSONHandler(MultiChildTree *pTree)
    {
       this->pTree = pTree;
       cppmempool::Retain(this->pTree);
       pCurNode = NULL;
    }

    virtual ~DocumentJSONHandler()
    {
       cppmempool::Release(this->pTree);
    }

    virtual void StartObject()
    {
       JSONObject *pJObj = new JSONObject();
       if (pTree->IsTreeEmpty()) {
          pTree->SetRootNode(pJObj);
       }
       else {
          __SetValue<JSONObject*>(pCurNode, new CFString(GetCurNVPairName()), pJObj);
       }
        
       pCurNode = pJObj;  
    }

    virtual void EndObject()
    {
       pCurNode = static_cast<JSONNode*>(pCurNode->GetParent());
    }

    virtual void StartNVPair(const CFString & nvPairName)
    {
       // nothing
    }

    virtual void ValueString(const CFString & v)
    {
       __SetValue<CFString*>(pCurNode, GetCurNodeName(), new CFString(v)); 
    }

    virtual void ValueInt(const int v)
    {
       __SetValue<int>(pCurNode, GetCurNodeName(), v);
    }

    virtual void ValueInt64(const i64_t v)
    {
       __SetValue<i64_t>(pCurNode, GetCurNodeName(), v);
    }

    virtual void ValueDouble(const double v)
    {
       __SetValue<double>(pCurNode, GetCurNodeName(), v);
    }

    virtual void ValueBoolean(const bool v)
    {
       __SetValue<bool>(pCurNode, GetCurNodeName(), v);
    }

    virtual void ValueNull()
    {
       if (pCurNode->GetNodeType() == JSONNode::JN_OBJECT)
          pCurNode->CastToJSONObject()->SetNull(new CFString(GetCurNVPairName()));
       else 
          pCurNode->CastToJSONArray()->AppendNull();
    }

    virtual void EndNVPair()
    {
       // nothing
    }

    virtual void StartArray()
    {
       JSONArray *pJArray = new JSONArray();
       __SetValue<JSONArray*>(pCurNode, GetCurNodeName(), pJArray);
       pCurNode = pJArray;
    }

    virtual void EndArray()
    {
       pCurNode = static_cast<JSONNode*>(pCurNode->GetParent());
    }

    virtual void Error(const CFString &msg)
    {
       throw JSONException(msg);
    }

    virtual void FatalError(const CFString &msg)
    {
       throw JSONException(msg);
    }

    virtual void Warning(const CFString &msg)
    {

    }

};

/**
 *  Get the root node
 */
JSONObject *JSONDocument::GetRootJSONObject()
{
   return static_cast<JSONObject*>(pTree->GetRootNode());
}

/**
 *  Get a JSON Document by a file path. Internally, it makes use of JSONQuickParser to do parsing.
 */
JSONDocument * JSONDocument::Parse(pcwstr_t jsonFilePath)
{
   JSONDocument *pDoc = new JSONDocument();
   DocumentJSONHandler handler(pDoc->pTree);
   JSONQuickParser parser(true); // error if duplicated names exist within the same object
   
   parser.Parse(jsonFilePath, handler);
   return pDoc;
}

/**
 *  Get a JSON Document by a file path. Internally, it makes use of JSONQuickParser to do parsing.
 */
JSONDocument * JSONDocument::Parse(Reader &reader)
{
   JSONDocument *pDoc = new JSONDocument();
   DocumentJSONHandler handler(pDoc->pTree);
   JSONQuickParser parser(true); // error if duplicated names exist within the same object
   
   parser.Parse(reader, handler);
   return pDoc;
}

} // end of namespace json

} // end of namespace cppflib
